﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using Aiemp.ObjectModel;

namespace RaisingForce.Raise.Hardware
{
    sealed public class HardwareRegistry :
        ObjectRegistry<RobotHardwareRegistryItem>
    {
        #region Constructors

        internal HardwareRegistry()
            : base()
        { }

        #endregion

        #region Public Methods

        [Pure]
        public RegistryKey GetUniqueKey(RegistryKey key)
        {
            RegistryKey result = key;
            int n = 2;
            while (this.ContainsKey(result))
            {
                result = string.Format("{0} {1}", key, n);
                n += 1;
            }
            return result;
        }

        public RegistryKey? TryRegisterType(
            Type type,
            out object unregisterToken)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            Contract.Ensures(
                Contract.Result<RegistryKey?>() == null
                && Contract.ValueAtReturn<object>(out unregisterToken) == null
                || Contract.Result<RegistryKey?>() != null
                && Contract.ValueAtReturn<object>(out unregisterToken) != null);
            Contract.EndContractBlock();
            RobotHardwareAttribute robotHardwareAttribute =
                (RobotHardwareAttribute)type
                .GetCustomAttributes(
                    typeof(RobotHardwareAttribute),
                    inherit: false)
                .FirstOrDefault();
            if (robotHardwareAttribute == null)
            {
                unregisterToken = null;
                return null;
            }
            RobotHardwareRegistryItem registryItem =
                new RobotHardwareRegistryItem(
                    this.GetUniqueKey(robotHardwareAttribute.Key),
                    type,
                    robotHardwareAttribute.GetInfo());
            this.RegisterItem(registryItem, out unregisterToken);
            return robotHardwareAttribute.Key;
        }

        public RegistryKey? TryRegisterType(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            Contract.EndContractBlock();
            object unregisterToken;
            return this.TryRegisterType(type, out unregisterToken);
        }

        public RegistryKey[] TryRegisterTypesInAssembly(
            Assembly assembly,
            out object[] unregisterTokens)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("type");
            }
            Contract.Ensures(Contract.Result<RegistryKey[]>() != null);
            Contract.Ensures(Contract.ValueAtReturn<object[]>(out unregisterTokens) != null);
            Contract.Ensures(Contract.Result<RegistryKey[]>().Length == Contract.ValueAtReturn<object[]>(out unregisterTokens).Length);
            Contract.EndContractBlock();
            List<object> resultingTokens = new List<object>();
            List<RegistryKey> resultingKeys = new List<RegistryKey>();
            foreach (Type type in assembly.GetExportedTypes())
            {
                object unregisterToken;
                RegistryKey? key =
                    this.TryRegisterType(type, out unregisterToken);
                if (key.HasValue)
                {
                    resultingKeys.Add(key.Value);
                    resultingTokens.Add(unregisterToken);
                }
            }
            unregisterTokens = resultingTokens.ToArray();
            return resultingKeys.ToArray();
        }

        public RegistryKey[] TryRegisterTypesInAssembly(Assembly assembly)
        {
            Contract.Requires(assembly != null);
            Contract.Ensures(Contract.Result<RegistryKey[]>() != null);
            object[] unregisterTokens;
            return this.TryRegisterTypesInAssembly(assembly, out unregisterTokens);
        }

        #endregion
    }
}
